

import sys
import time

import builtins

def global_reg(cls):
    name = cls.__name__
    builtins.__dict__[name] = cls
    return cls

global_reg(global_reg)

from direct.showbase.ShowBase import ShowBase
from panda3d.core import WindowProperties, FrameBufferProperties
from panda3d.core import GraphicsPipe, GraphicsOutput
from panda3d.core import Point3

@global_reg
class MyFrame(ShowBase):

    def __init__(self):
        ShowBase.__init__(self)
        self.size = self.win.get_size()
        self.hsize = (self.size[0] // 2, self.size[1] // 2)
        
        
        base.set_background_color(0.4, 0.4, 1, 1)
        self.camLens.set_near(0.1)
        self.camLens.set_fov(60)
        # ~ base.setFrameRateMeter(True)

        
        self.disableMouse()
        # ~ props = WindowProperties()
        # ~ props.set_cursor_hidden(True)
        # ~ self.win.request_properties(props)
        
        
        self.accept('f12', self.do_screenshot)
        taskMgr.add(self.update, 'updateWorld')
        
        self.page = None
        self.update_list = []
        self.mouse_update_list = []
        self.timers = []
        
        hx, hy = self.hsize
        self.win.movePointer(0, hx, hy)
        
        # ~ self.help_camera = self.cam
        

    def update(self, task):
        dt = globalClock.getDt()
        
        # mouse update
        md = self.win.getPointer(0)
        x = md.getX()
        y = md.getY()
        
        hx, hy = self.hsize
        if self.win.movePointer(0, hx, hy):
            dx = (x - hx)
            dy = (y - hy)
            dxy = (dx, dy)
            if dx != 0 or dy != 0 or 1:
                p0 = Point3()
                p1 = Point3()
                self.help_camera.node().get_lens().extrude(Point3(0, 0, 0), p0, p1)
                p0 = render.getRelativePoint(self.help_camera, p0)
                p1 = render.getRelativePoint(self.help_camera, p1)
                # ~ base.camLens.extrude(Point3(0, 0, 0), p0, p1)
                # ~ p0 = render.getRelativePoint(base.cam, p0)
                # ~ p1 = render.getRelativePoint(base.cam, p1)
                v = p1 - p0
                v.normalize()
                for f in self.mouse_update_list:
                    f(dxy, p0, v)
        
        # timer
        del_acc = []
        for t in self.timers:
            t[1] -= dt * t[3]
            if t[1] <= 0:
                r = t[2]()
                if r == True:
                    del_acc.append(t)
                t[1] = t[0]
        for t in del_acc:
            self.timers.remove(t)
        
        # updates        
        for f in self.update_list:
            f(dt)
        
        return task.cont

    def reg_timer(self, t, cb):
        record = [t, t, cb, 1]
        self.timers.append(record)
        return record
    
    def unreg_timer(self, timer):
        self.timers.remove(timer)
    
    def accept2(self, e):
        def foo(f):
            self.accept(e, f)
        return foo
        
    
    def do_screenshot(self):
        base.screenshot('Bullet')
    
    def __lt__(self, page):
        assert isinstance(page, Page)
        self.page = page
    
    def run(self):
        if self.page is None:
            raise Exception("Need a page to init")
        self.page.load(None)
        super().run()
    
    
    def makeFBO(self, name, auxrgba):
        # This routine creates an offscreen buffer.  All the complicated
        # parameters are basically demanding capabilities from the offscreen
        # buffer - we demand that it be able to render to texture on every
        # bitplane, that it can support aux bitplanes, that it track
        # the size of the host window, that it can render to texture
        # cumulatively, and so forth.
        winprops = WindowProperties()
        props = FrameBufferProperties()
        props.setRgbColor(True)
        props.setRgbaBits(8, 8, 8, 8)
        props.setDepthBits(1)
        props.setAuxRgba(auxrgba)
        return self.graphicsEngine.makeOutput(
            self.pipe, "model buffer", -2,
            props, winprops,
            GraphicsPipe.BFSizeTrackHost | GraphicsPipe.BFCanBindEvery |
            GraphicsPipe.BFRttCumulative | GraphicsPipe.BFRefuseWindow,
            self.win.getGsg(), self.win)


@global_reg
class Page:
    def load(self, mes: dict):
        raise NotImplementedError()
        
    def unload(self, mes: dict) -> dict:
        raise NotImplementedError()

class PageEmpty(Page):
    def load(self, mes):
        return {}
        
    def unload(self, mes):
        pass

@global_reg    
class Control:
    def reg(self):
        pass
        
    def unreg(self):
        pass


if __name__ == "__main__":
    game = MyFrame()
    game < PageEmpty()
    base.run()

